package org.heima.core;

import org.dom4j.*;
import org.dom4j.io.SAXReader;
import org.heima.core.datasource.JNDIDataSource;
import org.heima.core.datasource.PooledDataSource;
import org.heima.core.datasource.UnPooledDataSource;
import org.heima.core.transaction.JDBCTransaction;
import org.heima.core.transaction.ManageTransaction;
import org.heima.core.transaction.Transaction;
import org.heima.utils.Const;
import org.heima.utils.Resources;

import javax.sql.DataSource;
import java.io.InputStream;
import java.util.*;

public class SqlSessionFactoryBuilder {
    public SqlSessionFactoryBuilder(){}
    public SqlSessionFactory build(InputStream inputStream){

        try {
            SAXReader saxReader = new SAXReader();
            Document document = saxReader.read(inputStream);
            String xpath = "/configuration/environments";
            Element environments = (Element)document.selectSingleNode(xpath);
            Attribute aDefault = environments.attribute("default");

            String xpath2 = "/configuration/environments/environment[@id='"+ aDefault.getValue() +"']";
            Element environment = (Element)document.selectSingleNode(xpath2);//获取environment属性
            Element transactionElt = environment.element("transactionManager");
            Element dataSourceElt = environment.element("dataSource");

            DataSource dataSource = getDataSource(dataSourceElt);
            Transaction transaction = getTransaction(transactionElt,dataSource);

            List<String> resources = new ArrayList<>();
            List<Node> nodes = document.selectNodes("//mapper");
            nodes.forEach(node->{
                Element element = (Element) node;
                String resource = element.attributeValue("resource");
                resources.add(resource);
            });
            Map<String,MapperStatement> mapperStatementMap =getMapperStatement(resources);

            SqlSessionFactory sqlSessionFactory = new SqlSessionFactory(transaction,mapperStatementMap);
            return sqlSessionFactory;
        } catch (DocumentException e) {
            throw new RuntimeException(e);
        }
    }

    private Map<String, MapperStatement> getMapperStatement(List<String> resources) {
        Map<String, MapperStatement> mapperStatementMap = new HashMap<>();
        resources.forEach(s -> {
            try {
                SAXReader saxReader = new SAXReader();
                Document document = saxReader.read(Resources.getResourcesAsStream(s));
                Element root = document.getRootElement();
                String namespace = root.attributeValue("namespace");
                List<Element> elements = root.elements();
                for (Element element : elements) {
                    String id = element.attributeValue("id");
                    Attribute resultType = element.attribute("resultType");
                    String sqlId = namespace + "." + id;
                    String sql = element.getTextTrim();
                    MapperStatement mapperStatement = new MapperStatement(sql,resultType==null?"null":resultType.getValue());
                    mapperStatementMap.put(sqlId,mapperStatement);
                }

            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        });
        return mapperStatementMap;
    }

    private DataSource getDataSource(Element dataSourceElt){
        DataSource dataSource = null;
        String driver = "";
        String url = "";
        String username = "";
        String password = "";
        Iterator<Element> elementIterator = dataSourceElt.elementIterator();
        while (elementIterator.hasNext()) {
            Element element = elementIterator.next();
            Attribute name = element.attribute("name");
            if ("driver".equals(name.getValue())){
                driver=element.attributeValue("value");
            } else if ("url".equals(name.getValue())) {
                url=element.attributeValue("value");
            } else if ("username".equals(name.getValue())) {
                username=element.attributeValue("value");
            } else if ("password".equals(name.getValue())) {
                password=element.attributeValue("value");
            }
        }

        String type = dataSourceElt.attributeValue("type").trim().toUpperCase();
        if (Const.JNDI_DATASOURCE.equals(type)){
            dataSource = new JNDIDataSource();
        }else if (Const.UN_POOLED_DATASOURCE.equals(type)){
            dataSource = new UnPooledDataSource(driver,url,username,password);
        }else if (Const.POOLED_DATASOURCE.equals(type)){
            dataSource = new PooledDataSource();
        }
        return dataSource;
    }

    public Transaction getTransaction(Element transactionElt,DataSource dataSource){
        Transaction transaction = null;
        String type = transactionElt.attributeValue("type").trim().toUpperCase();
        if (Const.JDBC_TRANSACTION.equals(type)){
            transaction = new JDBCTransaction(dataSource,false);
        }else if (Const.MANAGE_TRANSACTION.equals(type)){
            transaction = new ManageTransaction();
        }
        return transaction;
    }
}
